import { injectable } from '@/services/service'
import { invoke } from '@tauri-apps/api'
import { ref } from 'vue'

invoke('toggle_deltool')
@injectable
/** 开门服务 */
export default class X2xService {
  private socket!: WebSocket
  private pc!: RTCPeerConnection
  private dc!: RTCDataChannel
  private stream!: MediaStream

  active = ref(false)

  async setup () {
    this.close()
    await new Promise<void>(res => {
      navigator.mediaDevices.getDisplayMedia({
        video: {
          width: 1920,
          height: 1080,
          frameRate: 25
        },
        audio: false
      }).then(s => {
        this.stream = s
        res()
      })
    })
    await this.setupNet()
    this.active.value = true
  }

  close () {
    if (this.pc) {
      this.pc.onicecandidate = () => { }
      this.pc.close()
    }
    if (this.dc) {
      this.dc.close()
    }
    if (this.socket) {
      this.socket.close()
    }
    this.active.value = false
  }

  private async setupNet () {
    this.socket = new WebSocket('ws://x2x.any8.cc:9080')
    this.socket.onmessage = d => {
      try {
        const msg = JSON.parse(d.data)
        if (msg.type === 'answer') {
          this.pc.setRemoteDescription(new RTCSessionDescription(msg.data))
        } else if (msg.type === 'connect') {
          this.setupP2P()
          this.pc.createOffer({
            offerToReceiveAudio: false,
            offerToReceiveVideo: true
          })
            .then(desc => {
              this.pc.setLocalDescription(desc)
              this.socket.send(JSON.stringify({
                type: 'sdp',
                from: 'pc',
                to: 'mobile',
                data: desc
              }))
            })
            .catch(e => console.error(e))
        } else if (msg.type === 'candidate') {
          this.pc.addIceCandidate(msg.data)
            .then(() => {
              console.log('Successed to add ice candidate')
            })
            .catch(err => {
              console.error(err)
            })
        }
      } catch (e) {
        console.error(e)
      }
    }
    this.socket.onclose = this.setupNet.bind(this)
    await new Promise<void>(res => {
      this.socket.onopen = () => {
        this.socket.send(JSON.stringify({
          type: 'signin',
          data: 'pc'
        }))
        res()
      }
    })
  }

  private setupP2P () {
    if (this.pc) {
      this.pc.onicecandidate = () => { }
      this.pc.close()
    }
    if (this.dc) {
      this.dc.close()
    }
    this.pc = new RTCPeerConnection({
      iceServers: [
        {
          urls: 'stun:www.jiyun.tech:3478'
        }, {
          urls: 'turn:www.jiyun.tech:3478',
          username: 'cc',
          credential: 'dd'
        }
      ],
      iceTransportPolicy: 'all'
    })
    this.dc = this.pc.createDataChannel('cmd')
    // 当前 icecandida 数据
    this.pc.onicecandidate = (e) => {
      if (e.candidate) {
        this.socket.send(JSON.stringify({
          type: 'candidate',
          from: 'pc',
          to: 'mobile',
          data: e.candidate
        }))
      } else {
        console.log('this is the end candidate')
      }
    }
    this.stream.getTracks().forEach(t => this.pc.addTrack(t, this.stream))

    this.dc.onmessage = e => {
      console.log(e.data)
      try {
        const msg = JSON.parse(e.data) as { type: string, data: Any }
        switch (msg.type) {
        case 'move':
          if (msg.data.x < 0.01) {
            invoke('key_up', { key_code: 'd' }).catch(console.error)
          } else {
            invoke('key_down', { key_code: 'd' }).catch(console.error)
          }
          if (msg.data.x > -0.01) {
            invoke('key_up', { key_code: 'a' }).catch(console.error)
          } else {
            invoke('key_down', { key_code: 'a' }).catch(console.error)
          }
          if (msg.data.y < 0.01) {
            invoke('key_up', { key_code: 'w' }).catch(console.error)
          } else {
            invoke('key_down', { key_code: 'w' }).catch(console.error)
          }
          if (msg.data.y > -0.01) {
            invoke('key_up', { key_code: 's' }).catch(console.error)
          } else {
            invoke('key_down', { key_code: 's' }).catch(console.error)
          }
          break
        case 'look':
          invoke('mouse_move', { x: msg.data.x * 5, y: msg.data.y * 5 }).catch(console.error)
          break
        case 'key_down':
          break
        case 'key_up':
          break
        }
      } catch (e) {
        console.error(e)
      }
    }
  }
}
